package xyz.hubery.util;

import xyz.hubery.enums.KlineType;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;

/**
 * K线时间工具类（基于 java.time）
 */
public class KlineTimeUtil {

    /**
     * 获取K线时间（返回 LocalDateTime）
     */
    public static LocalDateTime getKLineTime(KlineType klineType) {
        LocalDateTime now = LocalDateTime.now();

        switch (klineType) {
            case ONE_MINUTES:
                return now.truncatedTo(ChronoUnit.MINUTES);
            case FIVE_MINUTES:
                return now.withMinute((now.getMinute() / 5) * 5).truncatedTo(ChronoUnit.MINUTES);
            case FIFTEEN_MINUTES:
                return now.withMinute((now.getMinute() / 15) * 15).truncatedTo(ChronoUnit.MINUTES);
            case THIRTY_MINUTES:
                return now.withMinute((now.getMinute() / 30) * 30).truncatedTo(ChronoUnit.MINUTES);
            case ONE_HOUR:
                return now.truncatedTo(ChronoUnit.HOURS);
            case TWO_HOURS:
                return now.withHour((now.getHour() / 2) * 2).truncatedTo(ChronoUnit.HOURS);
            case FOUR_HOURS:
                return now.withHour((now.getHour() / 4) * 4).truncatedTo(ChronoUnit.HOURS);
            case SIX_HOURS:
                return now.withHour((now.getHour() / 6) * 6).truncatedTo(ChronoUnit.HOURS);
            case TWELVE_HOURS:
                return now.withHour((now.getHour() / 12) * 12).truncatedTo(ChronoUnit.HOURS);
            case ONE_DAY:
                return now.truncatedTo(ChronoUnit.DAYS);
            case ONE_WEEK:
                return now.truncatedTo(ChronoUnit.DAYS).with(java.time.DayOfWeek.MONDAY);
            case ONE_MONTH:
                return now.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1);
            case ONE_YEAR:
                return now.truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
            default:
                return now.truncatedTo(ChronoUnit.MINUTES);
        }
    }

    /**
     * 获取K线时间戳（毫秒）
     */
    public static Long getKLineTimestamp(KlineType klineType) {
        LocalDateTime time = getKLineTime(klineType);
        return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定时间的K线时间
     */
    public static LocalDateTime getKLineTime(LocalDateTime dateTime, KlineType klineType) {
        switch (klineType) {
            case ONE_MINUTES:
                return dateTime.truncatedTo(ChronoUnit.MINUTES);
            case FIVE_MINUTES:
                return dateTime.withMinute((dateTime.getMinute() / 5) * 5).truncatedTo(ChronoUnit.MINUTES);
            case FIFTEEN_MINUTES:
                return dateTime.withMinute((dateTime.getMinute() / 15) * 15).truncatedTo(ChronoUnit.MINUTES);
            case THIRTY_MINUTES:
                return dateTime.withMinute((dateTime.getMinute() / 30) * 30).truncatedTo(ChronoUnit.MINUTES);
            case ONE_HOUR:
                return dateTime.truncatedTo(ChronoUnit.HOURS);
            case TWO_HOURS:
                return dateTime.withHour((dateTime.getHour() / 2) * 2).truncatedTo(ChronoUnit.HOURS);
            case FOUR_HOURS:
                return dateTime.withHour((dateTime.getHour() / 4) * 4).truncatedTo(ChronoUnit.HOURS);
            case SIX_HOURS:
                return dateTime.withHour((dateTime.getHour() / 6) * 6).truncatedTo(ChronoUnit.HOURS);
            case TWELVE_HOURS:
                return dateTime.withHour((dateTime.getHour() / 12) * 12).truncatedTo(ChronoUnit.HOURS);
            case ONE_DAY:
                return dateTime.truncatedTo(ChronoUnit.DAYS);
            case ONE_WEEK:
                return dateTime.truncatedTo(ChronoUnit.DAYS).with(java.time.DayOfWeek.MONDAY);
            case ONE_MONTH:
                return dateTime.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1);
            case ONE_YEAR:
                return dateTime.truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
            default:
                return dateTime.truncatedTo(ChronoUnit.MINUTES);
        }
    }

    /**
     * 获取指定时间的K线时间戳
     */
    public static Long getKLineTimestamp(LocalDateTime dateTime, KlineType klineType) {
        LocalDateTime klineTime = getKLineTime(dateTime, klineType);
        return klineTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 计算下一个K线周期的时间
     */
    public static LocalDateTime getNextKLineTime(KlineType klineType) {
        LocalDateTime currentKlineTime = getKLineTime(klineType);

        switch (klineType) {
            case ONE_MINUTES:
                return currentKlineTime.plusMinutes(1);
            case FIVE_MINUTES:
                return currentKlineTime.plusMinutes(5);
            case FIFTEEN_MINUTES:
                return currentKlineTime.plusMinutes(15);
            case THIRTY_MINUTES:
                return currentKlineTime.plusMinutes(30);
            case ONE_HOUR:
                return currentKlineTime.plusHours(1);
            case TWO_HOURS:
                return currentKlineTime.plusHours(2);
            case FOUR_HOURS:
                return currentKlineTime.plusHours(4);
            case SIX_HOURS:
                return currentKlineTime.plusHours(6);
            case TWELVE_HOURS:
                return currentKlineTime.plusHours(12);
            case ONE_DAY:
                return currentKlineTime.plusDays(1);
            case ONE_WEEK:
                return currentKlineTime.plusWeeks(1);
            case ONE_MONTH:
                return currentKlineTime.plusMonths(1);
            case ONE_YEAR:
                return currentKlineTime.plusYears(1);
            default:
                return currentKlineTime.plusMinutes(1);
        }
    }

    /**
     * 检查时间是否属于同一个K线周期
     */
    public static boolean isSameKlinePeriod(LocalDateTime time1, LocalDateTime time2, KlineType klineType) {
        Long timestamp1 = getKLineTimestamp(time1, klineType);
        Long timestamp2 = getKLineTimestamp(time2, klineType);
        return timestamp1.equals(timestamp2);
    }

    /**
     * 获取K线周期的毫秒数
     */
    public static long getKlinePeriodMillis(KlineType klineType) {
        switch (klineType) {
            case ONE_MINUTES:
                return 60 * 1000L;
            case FIVE_MINUTES:
                return 5 * 60 * 1000L;
            case FIFTEEN_MINUTES:
                return 15 * 60 * 1000L;
            case THIRTY_MINUTES:
                return 30 * 60 * 1000L;
            case ONE_HOUR:
                return 60 * 60 * 1000L;
            case TWO_HOURS:
                return 2 * 60 * 60 * 1000L;
            case FOUR_HOURS:
                return 4 * 60 * 60 * 1000L;
            case SIX_HOURS:
                return 6 * 60 * 60 * 1000L;
            case TWELVE_HOURS:
                return 12 * 60 * 60 * 1000L;
            case ONE_DAY:
                return 24 * 60 * 60 * 1000L;
            case ONE_WEEK:
                return 7 * 24 * 60 * 60 * 1000L;
            case ONE_MONTH:
                // 近似值，实际月份天数不同
                return 30L * 24 * 60 * 60 * 1000L;
            case ONE_YEAR:
                // 近似值，实际年份天数不同
                return 365L * 24 * 60 * 60 * 1000L;
            default:
                return 60 * 1000L;
        }
    }

    /**
     * 获取K线周期描述
     */
    public static String getKlinePeriodDescription(KlineType klineType) {
        switch (klineType) {
            case ONE_MINUTES:
                return "1分钟";
            case FIVE_MINUTES:
                return "5分钟";
            case FIFTEEN_MINUTES:
                return "15分钟";
            case THIRTY_MINUTES:
                return "30分钟";
            case ONE_HOUR:
                return "1小时";
            case TWO_HOURS:
                return "2小时";
            case FOUR_HOURS:
                return "4小时";
            case SIX_HOURS:
                return "6小时";
            case TWELVE_HOURS:
                return "12小时";
            case ONE_DAY:
                return "1日";
            case ONE_WEEK:
                return "1周";
            case ONE_MONTH:
                return "1月";
            case ONE_YEAR:
                return "1年";
            default:
                return "未知";
        }
    }
}